import json
import sys

sys.path.append("../")
sys.path.append("../..")

from featurelib.feature_set.app.id.app_id_base_v2.AppIdBaseV2 import AppIdBaseV2
from featurelib.feature_set.app.id.app_id_gcate_v2.AppIdGCateV2 import AppIdGCateV2
from featurelib.feature_set.app.id.app_id_loan_v1.AppIdLoanV1 import AppIdLoanV1
from featurelib.feature_set.app.id.app_id_loan_v2.AppIdLoanV2 import AppIdLoanV2
from feature_set.app.id.app_id_gcate_v1.AppIdGCateV1 import AppIdGCateV1
from feature_set.app.id.app_id_ojk_v1.AppIdOJKV1 import AppIdOJKV1
from feature_set.app.id.app_id_comp_v1.AppIdCompV1 import AppIdCompV1
from feature_set.app.id.app_id_base_v1.AppIdBaseV1 import AppIdBaseV1
from feature_set.app.un.app_un_woe_v1.AppUnWoeV1 import AppUnWoeV1
from feature_set.sms.id.sms_id_base_v1.sms_id_base_v1 import SmsIdBaseV1
from feature_set.sms.un.sms_un_woe_v1.SmsUnWoeV1 import SmsUnWoeV1
from feature_set.cross.id.cross_id_base_v1.cross_id_base_v1 import CrossIdBaseV1
from feature_set.call.id.call_id_base0_v1.CallIdBase0V1 import CallIdBase0V1
from feature_set.loan.id.loan_id_base_v3_2.loan_id_base_v3_2 import LoanIdBaseV3_2
from feature_set.app.id.app_id_cate_v1.AppIdCateV1 import AppIdCateV1
from featurelib.common import *

feature_map = {
    "app_id_base_v1": AppIdBaseV1(),
    "app_id_comp_v1": AppIdCompV1(),
    "app_id_gcate_v1": AppIdGCateV1(),
    "app_id_loan_v1": AppIdLoanV1(),
    "app_id_base_v2": AppIdBaseV2(),
    "app_id_cate_v1": AppIdCateV1(),
    "app_id_gcate_v2": AppIdGCateV2(),
    "app_id_loan_v2": AppIdLoanV2(),
    "app_id_ojk_v1": AppIdOJKV1(),
    "app_id_woe_v1_a3": AppUnWoeV1(conf_path='app/id/app_id_woe_v1/a3'),
    "app_id_woe_v1_a5": AppUnWoeV1(conf_path='app/id/app_id_woe_v1/a5'),
    "sms_id_base_v1": SmsIdBaseV1(),
    'sms_id_woe_v1_a3': SmsUnWoeV1('sms/id/sms_id_woe_v1/a4', 'english'),
    'sms_id_woe_v1_a5': SmsUnWoeV1('sms/id/sms_id_woe_v1/a4', 'english'),
    "call_id_base0_v1": CallIdBase0V1(),
    'cross_id_base_v1': CrossIdBaseV1(),
    "loan_id_base_v3_2": LoanIdBaseV3_2()
}

import sys
import time
import traceback
import warnings
from concurrent.futures import ProcessPoolExecutor, as_completed

from flask import Flask, request
from nb_log import get_logger
import numpy as np

logger = get_logger(None)
app = Flask(__name__)


def featue_task(feature_generator, request_data: RequstData):
    try:
        rs = feature_generator.gen_features(request_data)
        return rs
    except Exception as e:
        logger.error(
            f"{request_data.order_id}特征模块{type(feature_generator)}计算失败,报错如下\n{traceback.format_exc()}")
        return {}


executor = ProcessPoolExecutor(max_workers=3)

import lightgbm as lgb

model_a2 = lgb.Booster(model_file='model_files/lgb_id_newmodel_v2.txt')
model_a5 = lgb.Booster(model_file='model_files/lgb_a5_score.txt')
model_third_v1 = lgb.Booster(model_file='model_files/lgb_model_third_v1.txt')
model_b1 = lgb.Booster(model_file='model_files/lgb_b1_0329.txt')

feaList_a2 = model_a2.feature_name()
feaList_a5 = model_a5.feature_name()
feaList_third = model_third_v1.feature_name()
feaList_b1 = model_b1.feature_name()

monitor_flist_a2 = model_features(model_a2).head(50)['var'].to_list()
monitor_flist_a5 = model_features(model_a5).head(50)['var'].to_list()
monitor_flist_third = model_features(model_third_v1).head(50)['var'].to_list()
monitor_flist_b1 = model_features(model_b1).head(50)['var'].to_list()


@app.route("/api/<path:model_name>", methods=["POST"])
def api(model_name):
    request_json = request.get_json()

    order_id, request_data = tranReq2featurelibData(request_json)

    task_list = [(gen_function, request_data, feature_name) for feature_name, gen_function in feature_map.items()]

    feature_dict = {}
    futures = {executor.submit(featue_task, gen_function, data): feature_name
               for gen_function, data, feature_name in task_list}

    third_feature = request_json['third_feature']
    phone_number = request_json['base_info']['phone']
    random_no = str(phone_number)[-1]
    new_old_sign = request_json['base_info']['new_old_sign']

    # 计算加载所有特征
    a = time.time()
    for future in as_completed(futures):
        feature_name = futures[future]
        data = future.result()
        new_dict = {f"{feature_name}_{k}": v for k, v in data.items()}
        feature_dict.update(new_dict)
    b = time.time()
    logger.info(f"{order_id}特征计算耗时:{b - a}s")

    if new_old_sign == 'OLD':  # 老客分支
        logger.info(f"{order_id},走老客分支，其base_info:{request_json['base_info']}")
        monite_flist = feaList_b1
        feature_list_b1 = []

        for f in feaList_b1:
            ff = feature_dict.get(f, -999)
            try:
                ff = float(ff)
            except:
                ff = -999
            feature_list_b1.append(ff)

        b1_prob = model_b1.predict([feature_list_b1])[0]
        b1_score = float(round(550 - 60 / np.log(2) * np.log(b1_prob / (1 - b1_prob)), 0))

        # 规则模块
        applicant = request_data.applicant
        creditfeature_v3 = third_feature['creditfeature_v3']
        creditfeature_v3_fea = creditfeature_v3.get("message",{}).get("featurelist",{})

        maxOverdueDays = applicant.get("historyOrderInfo",{}).get("maxOverdueDays",-999)
        lastOverdueDays = applicant.get("historyOrderInfo", {}).get("lastOverdueDays", -999)
        deductCnt = applicant.get("historyOrderInfo", {}).get("deductCnt", -999)

        userDeviceCount = applicant.get("userDeviceCount", -999)
        deviceIdSameWithLastInpaidOrder = applicant.get("deviceStatisticInfo", {}).get("deviceIdSameWithLastInpaidOrder", '')
        deviceSameIpPhoneCnt24h = applicant.get("deviceStatisticInfo", {}).get("deviceSameIpPhoneCnt24h", -999)
        deviceAndroidSameIdUserCnt24h = applicant.get("deviceStatisticInfo", {}).get("deviceAndroidSameIdUserCnt24h", -999)
        deviceSameIpPhoneCnt1h = applicant.get("deviceStatisticInfo", {}).get("deviceSameIpPhoneCnt1h", -999)

        relatedUserIn24h = applicant.get("emergencyContactRuleInfo", {}).get("relatedUserIn24h", -999)

        PA_PC = creditfeature_v3_fea.get("PA_PC",-999)
        PA_IC = creditfeature_v3_fea.get("PA_IC", -999)

        rule201 = maxOverdueDays>=1
        rule202 = lastOverdueDays>=1
        rule203 = userDeviceCount>=3
        rule204 = PA_PC>=3
        rule205 = PA_IC>=2
        rule206 = deviceSameIpPhoneCnt24h>=1
        rule207 = deviceIdSameWithLastInpaidOrder == 'False'
        rule208 = deviceAndroidSameIdUserCnt24h>=1
        rule209 = deductCnt>=1
        rule210 = deviceSameIpPhoneCnt1h>=1
        rule211 = relatedUserIn24h>=1
        rule212 = (b1_score >0 ) and (b1_score<577)

        if rule201 or rule202 or rule203 or rule204 or rule205 or rule206 or rule207 or rule208 or rule209 or rule210 or rule211 or rule212:
            score_a2 = 100
            prob_a2 = 1.1
        else:
            score_a2 = 900
            prob_a2 = 0.1

        monitor_features = {k: feature_dict.get(k, -9999) for k in monite_flist}
        monitor_features.update(
            {'b1_prob_real': b1_prob, 'b1_score_real': b1_score, 'random_no': random_no, "applicant": applicant,
             "creditfeature_v3": creditfeature_v3})

        reuslt = {"code": 200,
                "data": {'a2_prob': prob_a2,
                         'a2_score': score_a2},
                'monitor_features': monitor_features}
        logger.info(f"{order_id},{model_name},输出{json.dumps(reuslt, indent=4, ensure_ascii=False)}")
        return reuslt
    else:  # 新客分支
        logger.info(f"{order_id},走老客分支，其base_info:{request_json['base_info']}")
        monite_flist = list(set(monitor_flist_a2 + monitor_flist_a5))

        feature_list_a2 = []
        for f in feaList_a2:
            ff = feature_dict.get(f, -999)
            try:
                ff = float(ff)
            except:
                ff = -999
            feature_list_a2.append(ff)
        prob_a2 = model_a2.predict([feature_list_a2])[0]
        score_a2 = float(round(550 - 60 / np.log(2) * np.log(prob_a2 / (1 - prob_a2)), 0))

        feature_list_a5 = []
        for f in feaList_a5:
            ff = feature_dict.get(f, -999)
            try:
                ff = float(ff)
            except:
                ff = -999
            feature_list_a5.append(ff)
        prob_a5 = model_a5.predict([feature_list_a5])[0]
        score_a5 = float(round(550 - 60 / np.log(2) * np.log(prob_a5 / (1 - prob_a5)), 0))

        creditfeature_v3_dict = {}
        third_prob = -999
        third_score = -999
        try:
            creditfeature_v3_dict = request_json['third_feature']['creditfeature_v3']
            df = pd.json_normalize(creditfeature_v3_dict)
            df[feaList_third] = df[feaList_third].fillna(-999).astype(float)
            df['prob'] = model_third_v1.predict(df[feaList_third])
            df['score'] = df['prob'].map(prob2score)
            third_prob = float(df['prob'].iloc[0])
            third_score = float(df['score'].iloc[0])
        except:
            logger.error(f'creditfeature_v3模型未能正确计算:\n{traceback.format_exc()}')

        # 规则模块
        applicant = request_data.applicant

        monitor_features = {k: feature_dict.get(k, -9999) for k in monite_flist}

        monitor_features.update(
            {'a2_score_real': score_a2, 'a2_prob_real': prob_a2, 'a5_score_real': score_a5, 'a5_prob_real': prob_a5,
             'third_prob': third_prob, "third_score": third_score, 'random_no': random_no,
             "applicant": applicant, 'creditfeature_v3': creditfeature_v3_dict})

        source = applicant.get("source",0)
        relatedUserCount = applicant.get("emergencyContactRuleInfo", {}).get("relatedUserCount", -999)
        loanAppInstallWithin72h = applicant.get("appStatisticInfo", {}).get("loanAppInstallWithin72h", -999)
        gender = applicant.get("gender", "")

        if source == 0 : # 如果source 为0 全部拒绝
            score_a2 = 100
            prob_a2 = 1.1
        elif source == 2: # source 为2 则只走 score_a5 判断逻辑
            if (score_a5 <= 492)&(score_a5>=0):
                score_a2 = 100
                prob_a2 = 1.1
            else:
                score_a2 = 900
                prob_a2 = 0.1
        elif source in (1,3):
            rule11 = relatedUserCount >= 1
            rule12 = score_a5 <= 499
            rule13 = (score_a5 <= 518) and (third_score <= 543) and (third_score > 0)
            rule14 = (score_a5 <= 549) and (third_score <= 507) and (third_score > 0)
            rule15 = (score_a5 <= 542) and (loanAppInstallWithin72h >= 2)
            rule16 = (score_a5 <= 525) and (gender == "MALE")
            monitor_features.update({'rule11': rule11, 'rule12': rule12, 'rule13': rule13, 'rule14': rule14,
                                     'rule15': rule15, 'rule16': rule16})
            if rule11 or rule12 or rule13 or rule14 or rule15 or rule16:
                score_a2 = 100
                prob_a2 = 1.1
            else:
                score_a2 = 900
                prob_a2 = 0.1

        # 上一版本AB_TEST规则
        # if random_no in ['0', '1', '2', '3', '4']:
        #
        #     rule1 = score_a5 <= 499
        #     rule2 = relatedUserCount >= 1
        #     rule3 = (score_a5 <= 562) & (loanAppInstallWithin72h >= 2)
        #     rule4 = (score_a5 <= 539) & (gender == 'MALE')
        #
        #     monitor_features.update({'rule1': rule1, 'rule2': rule2, 'rule3': rule3, 'rule4': rule4})
        #
        #     if rule1 or rule2 or rule3 or rule4:
        #         score_a2 = 100
        #         prob_a2 = 1.1
        #     else:
        #         score_a2 = 900
        #         prob_a2 = 0.1
        #
        # else:  # 随机50%
        #     rule11 = relatedUserCount >= 1
        #     rule12 = score_a5 <= 499
        #     rule13 = (score_a5 <= 518) and (third_score <= 543) and (third_score > 0)
        #     rule14 = (score_a5 <= 549) and (third_score <= 507) and (third_score > 0)
        #     rule15 = (score_a5 <= 542) and (loanAppInstallWithin72h >= 2)
        #     rule16 = (score_a5 <= 525) and (gender == "MALE")
        #     monitor_features.update({'rule11': rule11, 'rule12': rule12, 'rule13': rule13, 'rule14': rule14,
        #                              'rule15': rule15, 'rule16': rule16})
        #     if rule11 or rule12 or rule13 or rule14 or rule15 or rule16:
        #         score_a2 = 100
        #         prob_a2 = 1.1
        #     else:
        #         score_a2 = 900
        #         prob_a2 = 0.1

        c = time.time()
        logger.info(f"{order_id},{model_name},模型计算耗时:{c - b}s")

        reuslt = {"code": 200,
                "data": {'a2_prob': prob_a2,
                         'a2_score': score_a2},
                'monitor_features': monitor_features}

        logger.info(f"{order_id},{model_name},输出{json.dumps(reuslt, indent=4,ensure_ascii=False)}")

        return reuslt


if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0', port=8089)
